import { readFileSync } from 'fs-extra';
import { merge } from 'lodash';
import { join } from 'path';
import { ExtensionContext } from 'vscode';
import { ModelItem } from '../interface';

export type ModelJson = { [key: string]: ModelItem | string[] };

/**
 * 模型对象加载帮助
 *
 * @author chitanda
 * @date 2022-01-13 13:01:34
 * @export
 * @class Model
 */
export class Model {
  private static readonly instance: Model = new Model();

  /**
   * 模型描述文件内容缓存
   *
   * @author chitanda
   * @date 2022-01-19 15:01:30
   * @protected
   * @type {Map<string, ModelJson>}
   */
  protected jsonCache: Map<string, ModelJson> = new Map();

  /**
   * 插件上下文
   *
   * @author chitanda
   * @date 2022-01-19 15:01:26
   * @protected
   * @type {ExtensionContext}
   */
  protected context!: ExtensionContext;

  /**
   * 设置导出上下文
   *
   * @author chitanda
   * @date 2022-01-19 15:01:33
   * @param {ExtensionContext} context
   */
  setContext(context: ExtensionContext) {
    this.context = context;
  }

  /**
   * 获取指定路径模型内容
   *
   * @author chitanda
   * @date 2022-01-19 15:01:56
   * @param {string} jsonPath
   * @return {*}  {ModelJson}
   */
  getModel(jsonPath: string): ModelJson {
    return this.getJson(jsonPath);
  }

  /**
   * 获取模型 json 文件内容
   *
   * @author chitanda
   * @date 2022-01-13 14:01:03
   * @protected
   * @param {string} jsonPath
   * @return {*}  {IModel}
   */
  protected getJson(jsonPath: string): IModel {
    const path = this.calcPath(jsonPath);
    if (this.jsonCache.has(path)) {
      return this.jsonCache.get(path)!;
    }
    const jsonStr = readFileSync(path, 'utf-8');
    let data = JSON.parse(jsonStr);
    data = this.fillExtends(data);
    if (data) {
      this.jsonCache.set(path, data);
    }
    return data;
  }

  /**
   * 填充继承内容
   *
   * @author chitanda
   * @date 2022-01-13 14:01:50
   * @protected
   * @param {ModelJson} data
   * @return {*}  {ModelJson}
   */
  protected fillExtends(data: ModelJson): ModelJson {
    const exts = data.extends as string[];
    if (exts && exts.length > 0) {
      for (let i = 0; i < exts.length; i++) {
        const item = exts[i];
        const extendPath = this.calcPath(item);
        const extendStr = readFileSync(extendPath, 'utf-8');
        let extendData = JSON.parse(extendStr);
        delete data.extends;
        extendData = this.fillExtends(extendData);
        data = merge(data, extendData);
      }
    }
    return data;
  }

  /**
   * 计算文件路径
   *
   * @author chitanda
   * @date 2022-01-19 15:01:33
   * @protected
   * @param {string} path
   * @return {*}  {string}
   */
  protected calcPath(path: string): string {
    return join(this.context.extensionPath, 'resources/model', `.${path}.json`);
  }

  /**
   * 实例
   *
   * @author chitanda
   * @date 2022-01-13 14:01:35
   * @static
   * @return {*}  {Model}
   */
  static getInstance(): Model {
    return this.instance;
  }
}
